// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

export default class Util {
    constructor() {
        this.bindEvents = [];
    }

    /**
     * 监听事件
     * @param {object} target 监听目标对象
     * @param {string} name 事件名称
     * @return {object} fn 事件处理函数
     */
    bind(target = window, name, fn) {
        if (!target.addEventListener || !name || !fn) {
            return false;
        }

        this.bindEvents.push({target, name, fn});
        target.addEventListener(name, fn, false);
        return true;
    }

    /**
     * 解除事件监听
     * @param {object} target 监听目标对象，可选
     * @param {string} name 事件名称，可选
     */
    unbind(target, name) {
        for (let i = this.bindEvents.length - 1; i >= 0; i--) {
            let item = this.bindEvents[i];
            if (item.target.removeEventListener
                && ((target && target === item.target) || !target)
                && ((name && name === item.name) || !name)) {
                item.target.removeEventListener(item.name, item.fn, false);
                this.bindEvents.splice(i, 1);
            }
        }
    }

    getUrlSearchVal(key) {
        let search = window.location.search.substr(1);
        let kv = search.split('&').map(kvs => {
            let [k, v] = kvs.split('=');
            return {k, v};
        }).find(item => item.k === key);
        return kv && decodeURIComponent(kv.v);
    }

    /**
     * 节流
     * @param {function} fn 要执行的方法
     * @param {number} delay 间隔时间
     */
    throttle(fn, delay, context) {
        let prev = Date.now();
        return function () {
            let args = arguments;
            if (Date.now() - prev >= delay) {
                fn.apply(context, args);
                prev = Date.now();
            }
        };
    }

    encodeUTF8(textStr) {
        const encodedStr = encodeURIComponent(textStr);
        const encodedArray = [];
        for (let i = 0; i < encodedStr.length; i++) {
            let cur = encodedStr.charAt(i);
            if (cur === '%') {
                let code = encodedStr.charAt(i + 1) + encodedStr.charAt(i + 2);
                encodedArray.push(parseInt(code, 16));
                i += 2;
            } else {
                encodedArray.push(cur.charCodeAt(0));
            }
        }

        return encodedArray;
    }

    decodeUTF8(utf8Array, start, end) {
        const len = utf8Array.length;
        start = start || 0;
        end = end || len;
        if (start < 0 || start >= len || end < 0 || end > len) {
            return '';
        }

        let encodedStr = '';
        for (let i = start; i < end; i++) {
            encodedStr += '%' + utf8Array[i].toString(16);
        }

        return decodeURIComponent(encodedStr);
    }

    createEl(tagName = 'div', options) {
        if (typeof tagName != 'string') {

          return null;
        }
        let el = document.createElement(tagName, options);
        return el;
    }

    createCanvas = (isMobile, ratio) => {
        const parentContainer = document.getElementById('container');
        let playerHeight = parentContainer && parentContainer.clientHeight;
        let playerWidth = playerHeight * ratio;
        if (isMobile) {
            playerWidth = parentContainer && parentContainer.clientWidth;
        }
        const $ele = this.createEl('canvas');
        $ele.style.display = 'block';
        $ele.width = playerWidth;
        $ele.height = playerHeight;
        $ele.id = 'playCanvas';
        return $ele
    }

    loadWasm(url) {
        return new Promise((resolve, reject) => {
            const wasmXHR = new XMLHttpRequest();
            wasmXHR.open('GET', url, true);
            wasmXHR.responseType = 'arraybuffer';
            wasmXHR.onload = function() {
                resolve(wasmXHR.response);
            };
            wasmXHR.onerror = function() {
                reject(`error${wasmXHR.status}`);
            };
            wasmXHR.send(null);
        })
    }

    convertImageDataToYUV420(imageData) {
        const imageWidth = imageData.width;
        const imageHeight = imageData.height;
        const originData = imageData.data;
        const yuv420Data = new Uint8Array(imageWidth * imageHeight * 3 / 2);
        const yData = yuv420Data.subarray(0, imageWidth * imageHeight);
        const uData = yuv420Data.subarray(yData.length, yData.length + imageWidth * imageHeight / 4);
        const vData = yuv420Data.subarray(yData.length + uData.length);

        for(let i=0; i<imageHeight; i++) {
            for(let j=0; j<imageWidth; j++) {
                const index = (i * imageWidth + j) * 4;
                const r = originData[index];
                const g = originData[index + 1];
                const b = originData[index + 2];

                const y = 0.299 * r + 0.587 * g + 0.114 * b;
                const u = -0.169 * r - 0.331 *g + 0.5 * b + 128;
                const v = 0.5 * r - 0.419 * g - 0.081 * b + 128;

                yData[i * imageWidth + j] = y;
                if(i % 2 === 0 && j % 2 === 0) {
                    uData[i / 2 * imageWidth / 2 + j / 2] = u;
                    vData[i / 2 * imageWidth / 2 + j / 2] = v;
                }
            }
        }

        return yuv420Data;
    }

    /**
     * 
     * @param {object} array Unit8Array数组 
     * @param {number} index 要取出的unit16在数组中的起始位置 
     * @returns 
     */
    getUnit16FromArray(array, index) {
        return (array[index] << 8) | array[index + 1];
    }

    /**
     * 
     * @param {object} array Unit8Array数组 
     * @param {number} index 要取出的unit32在数组中的起始位置 
     * @returns 
     */
    getUnit32FromArray(array, index) {
        return (array[index] << 24) |
            (array[index + 1] << 16) |
            (array[index + 2] << 8) |
            array[index + 3];
    }

    /**
     * @param {number} num 需要转换的数字
     * @returns 能被8整除的数
     */
    changeToMultipleOfEight(num) {
        if (num % 8 === 0) {
            return num;
        } else {
            return Math.ceil(num / 8) * 8;
        }
    }
}

Util.saveAsFile = (() => {
    let size = 50 * 1024 * 1024;
    let fileBuf = null;
    let pos = 0;
    let downloadOver = false;
    let needHeader = false;
    const headerLength = 8;
    return frame => {
        fileBuf = fileBuf || new Uint8Array(size);
        if (!downloadOver && frame) {
            let pkgLen = frame.length + (needHeader ? headerLength : 0);
            if (pos + pkgLen < size) {
                let pkg = new Uint8Array(pkgLen);
                if (needHeader) {
                    let header = Uint8Array.from([0x00, 0x00, 0x00, 0x03, (frame.length >> 24 & 0xFF), (frame.length >> 16 & 0xFF), (frame.length >> 8 & 0xFF), frame.length & 0xFF]);
                    pkg.set(header);
                    pkg.set(frame, headerLength);
                } else {
                    pkg.set(frame);
                }
                
                fileBuf.set(pkg, pos);
                pos += pkg.length;
            } else {
                let data = new Blob([fileBuf.buffer], {type: 'application/octet-stream'});
                let downloadUrl = window.URL.createObjectURL(data);
                let anchor = document.createElement('a');
                anchor.href = downloadUrl;
                anchor.download = 'casVideo.h264';
                document.body.appendChild(anchor);
                anchor.click();
                // 然后移除
                document.body.removeChild(anchor);
                window.URL.revokeObjectURL(data);
                downloadOver = true;
            }
        }
    };
})();
